
/**
*	\file
*	Defines the sun::window class.
*	\author Sean Tromnas
*/

#include <sun_pre_compiled.hpp>
#include <sun/window/window.hpp>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#include <sun/exception/exception.hpp>
#include <luabind/iterator_policy.hpp>
#include <sun/lua/function_proxy/function_proxy.hpp>
#endif

namespace sun
{

	namespace detail
	{

#if !defined(SUN_DISABLE_LUA)

		/** Exception thrown if Lua attempts to register a callback to a object which is not a function. */
		class invalid_callback : public exception
		{
		public:
			invalid_callback( std::string const & function )
				:	exception( nmn::create_string( "sun::detail::invalid_callback: Non-function provided as callback to %1%", function ) )
			{}
		};

		/** Allows lua to call the "at" member of an extension_name_vector_type. */
		std::string & extension_name_vector_at( sun::window::extension_name_vector_type * vec, size_t i )
		{
			return vec->at( i );
		}

		/** Allows lua to call the "push_back" member of an extension_name_vector_type. */
		void extension_name_vector_add( sun::window::extension_name_vector_type * vec, std::string const & val )
		{
			vec->push_back( val );
		}

		/** Allows lua to retreive the size of an sc++l vector. */
		unsigned long extension_name_vector_size( sun::window::extension_name_vector_type * vec )
		{
			return static_cast<unsigned long>( vec->size( ) );
		}

		/** Allows lua to iterate over the extension_name_vector_type as if it were a table. */
		sun::window::extension_name_vector_type & extension_name_vector_iterate( sun::window::extension_name_vector_type * vec )
		{
			return *vec;
		}

		/** Allows lua to connect to "window::notify_focus_change" */
		sun::window::signal_connection_type notify_focus_change( sun::window * w, luabind::object const & function )
		{
			if ( luabind::type( function ) != LUA_TFUNCTION )
			{
				throw invalid_callback( __FUNCTION__ );
			}

			return w->notify_focus_change( lua::function_proxy<bool>( function ) );
		}

		/** Allows lua to connect to "window::notify_quit" */
		sun::window::signal_connection_type notify_quit( sun::window * w, luabind::object const & function )
		{
			if ( luabind::type( function ) != LUA_TFUNCTION )
			{
				throw invalid_callback( __FUNCTION__ );
			}

			return w->notify_quit( lua::function_proxy<bool>( function ) );
		}

		/** Allows lua to connect to "window::notify_analogue_changed" */
		sun::window::signal_connection_type notify_analogue_changed( sun::window * w, luabind::object const & function )
		{
			if ( luabind::type( function ) != LUA_TFUNCTION )
			{
				throw invalid_callback( __FUNCTION__ );
			}

			return w->notify_analogue_changed( lua::function_proxy<bool>( function ) );
		}

		/** Allows lua to connect to "window::notify_button_changed" */
		sun::window::signal_connection_type notify_button_changed( sun::window * w, luabind::object const & function )
		{
			if ( luabind::type( function ) != LUA_TFUNCTION )
			{
				throw invalid_callback( __FUNCTION__ );
			}

			return w->notify_button_changed( lua::function_proxy<bool>( function ) );
		}
#endif

	};

	/**	Default constructor for the window settings. */
	window::window_settings::window_settings( )
		:		title( "" )
			,	width( 0 )
			,	height( 0 )
			,	colour_bits( 0 )
			,	depth_bits( 0 )
			,	stencil_bits( 0 )
			,	samples( 1 )
			,	fullscreen( false )
	{}

	//
	//	Static Member Functions
	//

	/**
	Exposes the window singleton to lua.
	@param  L The lua_State being initialised.
	\author Sean Tromans
	*/
	void window::lua_interface( lua_State * L )
	{
#if !defined(SUN_DISABLE_LUA)

		std::string & (sun::window::extension_name_vector_type::*get_string_at)( sun::window::extension_name_vector_type::size_type i ) = &sun::window::extension_name_vector_type::at;
		void (sun::window::extension_name_vector_type::*add_extension)( std::string const & ) = &sun::window::extension_name_vector_type::push_back;

		rainbow::graphics_device & (sun::window::*get_device)( ) = &sun::window::device;

		luabind::module(L,"sun")
		[
			luabind::class_<sun::window>("window")
				.scope
				[
					luabind::class_<sun::window::extension_name_vector_type>( "extension_name_vector_type" )
						.def( "at",			get_string_at )
						.def( "add",		add_extension )
						.property( "size",	&detail::extension_name_vector_size )
						.property( "values",&detail::extension_name_vector_iterate, luabind::return_stl_iterator ),

					luabind::class_<sun::window::window_settings>("window_settings")
						.def( luabind::constructor<>( ) )
						.def_readwrite( "title",		&window_settings::title )
						.def_readwrite( "width",		&window_settings::width )
						.def_readwrite( "height",		&window_settings::height )
						.def_readwrite( "colour_bits",	&window_settings::colour_bits )
						.def_readwrite( "depth_bits",	&window_settings::depth_bits )
						.def_readwrite( "stencil_bits",	&window_settings::stencil_bits )
						.def_readwrite( "samples",		&window_settings::samples )
						.def_readwrite( "fullscreen",	&window_settings::fullscreen )
						.def_readwrite( "extensions",	&window_settings::extensions )
				]
				.def( "create_window",				&window::create_window )
				.def( "notify_focus_change",		&detail::notify_focus_change )
				.def( "notity_quit",				&detail::notify_quit )
				.def( "notify_analogue_changed",	&detail::notify_analogue_changed )
				.def( "notify_button_changed",		&detail::notify_button_changed )
				.def( "show_cursor",				&window::show_cursor )
				.def( "grab_cursor",				&window::grab_cursor )
				.def( "pump_messages",				&window::pump_messages )
				.def( "present",					&window::present )
				.def( "close_window",				&window::close_window )
				.property( "device",				get_device ),

			luabind::def("get_window", &window::get_singleton )
		];
#else
		NMN_UNUSED( L );
#endif
	}
}